package com.yikome.product.service;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.yikome.common.CommonService;
import com.yikome.constant.StatusConstant;
import com.yikome.product.crawler.api99.dto.Product1688;
import com.yikome.product.crawler.api99.dto.Product1688SkuPropValue;
import com.yikome.product.crawler.api99.dto.Product1688SkuProps;
import com.yikome.product.crawler.api99.dto.ProductItemSku;
import com.yikome.product.crawler.api99.dto.ProductProp;
import com.yikome.product.crawler.api99.dto.ProductPropValue;
import com.yikome.product.crawler.api99.dto.ProductTmallAndTaobao;
import com.yikome.product.crawler.apiyk.dto.ProductPdd;
import com.yikome.product.crawler.apiyk.dto.ProductPddAttr;
import com.yikome.product.crawler.apiyk.dto.ProductPddProperty;
import com.yikome.product.crawler.apiyk.dto.ProductPddSku;
import com.yikome.product.dto.ProductAttributeEditDTO;
import com.yikome.product.dto.ProductAttributeValueCreateDTO;
import com.yikome.product.dto.ProductAttributeValueEditDTO;
import com.yikome.product.dto.ProductBatchEditDTO;
import com.yikome.product.dto.ProductDTO;
import com.yikome.product.dto.ProductEditDTO;
import com.yikome.product.dto.ProductExtEditDTO;
import com.yikome.product.dto.ProductSetEditDTO;
import com.yikome.product.dto.ProductSkuEditDTO;
import com.yikome.product.entity.Product;
import com.yikome.product.entity.ProductAttribute;
import com.yikome.product.entity.ProductAttributeValue;
import com.yikome.product.entity.ProductComments;
import com.yikome.product.entity.ProductExt;
import com.yikome.product.entity.ProductSet;
import com.yikome.product.entity.ProductSku;
import com.yikome.product.entity.Supplier;
import com.yikome.product.repository.ProductAttributeRepository;
import com.yikome.product.repository.ProductAttributeValueRepository;
import com.yikome.product.repository.ProductCommentsRepository;
import com.yikome.product.repository.ProductExtRepository;
import com.yikome.product.repository.ProductRepository;
import com.yikome.product.repository.ProductSetRepository;
import com.yikome.product.repository.ProductSkuRepository;
import com.yikome.product.repository.SupplierRepository;
import com.yikome.product.request.ProductRequest;
import com.yikome.shop.entity.CollectionProduct;
import com.yikome.shop.repository.CollectionProductRepository;
import com.yikome.shop.zhconverter.ZHConverter;
import com.yikome.utils.BeanUtils;
import com.yikome.utils.BoheUtils;
import com.yikome.utils.JSONUtils;
import com.yikome.utils.NamedParameterJdbcUtils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ProductService extends CommonService<Product, Long> {

	@Autowired
    private ProductRepository productRepository;
	
	@Autowired
    private ProductExtRepository productExtRepository;
	
	@Autowired
	private ProductAttributeRepository productAttributeRepository;
	
	@Autowired
	private ProductAttributeValueRepository productAttributeValueRepository;
	
	@Autowired
	private ProductSkuRepository productSkuRepository;
	
	@Autowired
	private ProductSetRepository productSetRepository;

	@Autowired
    private SupplierRepository supplierRepository;
	
	@Autowired
	private CollectionProductRepository collectionProductRepository ; 
	
	@Autowired
	private ProductCommentsRepository productCommentsRepository ; 
	
	@Autowired
    private BoheUtils boheUtils;
	
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    
    private static final BigDecimal originalMultiplicand = BigDecimal.valueOf(1.5);
    
    @Autowired
	public void setResourceDao(ProductRepository repository) {
		super.setCommonDao(repository);
	}
    
    /**
     * 分页获取产品.
     */
    public Page<ProductDTO> findProduct(ProductRequest productRequest, Pageable pageable){
    	
    	String contentSQL = "SELECT \n" + 
			    			"	p_product.id, p_product.title, p_product.image, p_product.description, p_product.tags, p_product.`status`, p_product.price, p_product.original_price, p_product.cover_img, \n" + 
			    			"	p_product_ext.source_type, s_supplier.shop_name supplier_name, p_product_ext.comments, p_product_ext.source_url, p_product_ext.created_by \n" + 
			    			"FROM p_product  \n"  ; 
    	
    	StringBuilder joinSQL = new StringBuilder("	LEFT JOIN p_product_ext ON (p_product.id = p_product_ext.product_id) \n");
    	joinSQL.append("	LEFT JOIN s_supplier ON (p_product_ext.supplier_id = s_supplier.id) \n");
    	
    	Map<String , Object> paramsMap = new HashMap<>();
    	StringBuilder paramsSQL = new StringBuilder("WHERE p_product.yn = 1 \n") ;
    	
        if(StrUtil.isNotBlank(productRequest.getProductId())) {
            List<String> productIds = StrUtil.split(productRequest.getProductId().replaceAll("\\s", ","), ',');
            if(productIds.size() == 1) {
                paramsMap.put("productId", productRequest.getProductId());
                paramsSQL.append("    AND p_product.id = :productId \n");
            }else {
                paramsMap.put("productIds", productIds);
                paramsSQL.append("    AND p_product.id IN (:productIds) \n");
            }
        }
    	if(StrUtil.isNotEmpty(productRequest.getTags())) {
    		paramsSQL.append("AND p_product.tags LIKE :productTag \n");
    		paramsMap.put("productTag", "%" + productRequest.getTags() + "%");
    	}
    	if(StrUtil.isNotEmpty(productRequest.getCreatedBy())) {
    		paramsSQL.append("AND p_product_ext.created_by LIKE :createdBy \n");
    		paramsMap.put("createdBy", "%" + productRequest.getCreatedBy());
    	}
    	if(productRequest.getStatus() != null) {
    		paramsSQL.append("AND p_product.`status` = :productStatus \n");
    		paramsMap.put("productStatus", productRequest.getStatus());
    	}
    	
    	if(productRequest.getMinPrice() != null) {
    		paramsSQL.append("AND p_product.price >= :minPrice \n");
    		paramsMap.put("minPrice", productRequest.getMinPrice());
    	}
    	if(productRequest.getMaxPrice() != null) {
    		paramsSQL.append("AND p_product.price <= :maxPrice \n");
    		paramsMap.put("maxPrice", productRequest.getMaxPrice());
    	}
    	
    	if(StrUtil.isNotEmpty(productRequest.getComments())) {
    		paramsSQL.append("AND p_product_ext.comments LIKE :productComments \n");
    		paramsMap.put("productComments", "%" + productRequest.getComments() + "%");
    	}
    	if(StrUtil.isNotBlank(productRequest.getProductTitle())) {
    		paramsSQL.append("AND p_product.title LIKE :productTitle \n");
    		paramsMap.put("productTitle", "%" + productRequest.getProductTitle() + "%");
    	}
    	
    	if(StrUtil.isNotBlank(productRequest.getSourceType())) {
    		paramsSQL.append("AND p_product_ext.source_type = :sourceType \n");
    		paramsMap.put("sourceType", productRequest.getSourceType());
    	}
    	if(productRequest.getSupplierId() != null) {
    		paramsSQL.append("AND s_supplier.id = :supplierId \n");
    		paramsMap.put("supplierId", productRequest.getSupplierId());
    	}
    	if(StrUtil.isNotBlank(productRequest.getCategoryIds())) {
    		List<String> categoryIdLists = StrUtil.split(productRequest.getCategoryIds(), ',');
    		if(categoryIdLists != null && !categoryIdLists.isEmpty()) {
    			paramsSQL.append("AND p_product_ext.category_id in (:categoryIds) \n");
        		paramsMap.put("categoryIds", categoryIdLists);
    		}
    	}
    	if(productRequest.getCategoryId() != null) {
    		paramsSQL.append("AND p_product_ext.category_id = :categoryId \n");
    		paramsMap.put("categoryId", productRequest.getCategoryId());
    	}
    	
    	if(productRequest.getBegainTime() != null && productRequest.getEndTime() != null) {
    		paramsSQL.append("AND p_product.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
    		paramsMap.put("begainTime", productRequest.getBegainTime());
    		paramsMap.put("endTime", productRequest.getEndTime());
    	}
    	
    	String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
    	String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
    	
    	List<ProductDTO> productDTOs = jdbcTemplate.query(contentSQL + joinSQL + paramsSQL + orderSQL + pageSQL , paramsMap, new BeanPropertyRowMapper<ProductDTO>(ProductDTO.class));
    	
    	for (ProductDTO productDTO : productDTOs) {
    		productDTO.setStatusName(StatusConstant.statusName(productDTO.getStatus()));
    		
    		productDTO.setProductSets(productSetRepository.findByProductId(productDTO.getId()));
    		
		}
    	
    	Long total = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM (" + contentSQL + joinSQL + paramsSQL + ") T", paramsMap, Long.class);

        return new PageImpl<>(productDTOs, pageable, total);
    	
    }
    
    public List<Map<String, Object>> exportFbLists(ProductRequest productRequest, Pageable pageable){
    	
    	String contentSQL = "SELECT \n" + 
			    			"	p_product.id, p_product.title, p_product.image, p_product.description, p_product.tags, p_product.`status`, p_product.price, p_product.original_price, p_product.cover_img, \n" + 
			    			"	p_product_ext.source_type, s_supplier.shop_name supplier_name, p_product_ext.comments, p_product_ext.source_url, p_product_ext.created_by, c_category.name category_name  \n" + 
			    			"FROM p_product  \n" +
			    			"	LEFT JOIN p_product_ext ON (p_product.id = p_product_ext.product_id) \n" +
			    			"	LEFT JOIN c_category ON (p_product_ext.category_id = c_category.id) \n" +
			    			"	LEFT JOIN s_supplier ON (p_product_ext.supplier_id = s_supplier.id) \n" ;
    	
    	Map<String , Object> paramsMap = new HashMap<>();
    	StringBuilder paramsSQL = new StringBuilder("WHERE p_product.yn = 1 AND p_product.`status` = 10 \n") ;
    	
    	if(StrUtil.isNotBlank(productRequest.getProductId())) {
            List<String> productIds = StrUtil.split(productRequest.getProductId().replaceAll("\\s", ","), ',');
            if(productIds.size() == 1) {
                paramsMap.put("productId", productRequest.getProductId());
                paramsSQL.append("    AND p_product.id = :productId \n");
            }else {
                paramsMap.put("productIds", productIds);
                paramsSQL.append("    AND p_product.id IN (:productIds) \n");
            }
        }

    	if(StrUtil.isNotEmpty(productRequest.getTags())) {
    		paramsSQL.append("AND p_product.tags LIKE :productTag \n");
    		paramsMap.put("productTag", "%" + productRequest.getTags() + "%");
    	}
    	if(StrUtil.isNotEmpty(productRequest.getCreatedBy())) {
    		paramsSQL.append("AND p_product_ext.created_by LIKE :createdBy \n");
    		paramsMap.put("createdBy", "%" + productRequest.getCreatedBy());
    	}
    	if(productRequest.getStatus() != null) {
    		paramsSQL.append("AND p_product.`status` = :productStatus \n");
    		paramsMap.put("productStatus", productRequest.getStatus());
    	}
    	
    	if(productRequest.getMinPrice() != null) {
    		paramsSQL.append("AND p_product.price >= :minPrice \n");
    		paramsMap.put("minPrice", productRequest.getMinPrice());
    	}
    	if(productRequest.getMaxPrice() != null) {
    		paramsSQL.append("AND p_product.price <= :maxPrice \n");
    		paramsMap.put("maxPrice", productRequest.getMaxPrice());
    	}
    	
    	if(StrUtil.isNotEmpty(productRequest.getComments())) {
    		paramsSQL.append("AND p_product_ext.comments LIKE :productComments \n");
    		paramsMap.put("productComments", "%" + productRequest.getComments() + "%");
    	}
    	if(StrUtil.isNotBlank(productRequest.getProductTitle())) {
    		paramsSQL.append("AND p_product.title LIKE :productTitle \n");
    		paramsMap.put("productTitle", "%" + productRequest.getProductTitle() + "%");
    	}
    	
    	if(StrUtil.isNotBlank(productRequest.getSourceType())) {
    		paramsSQL.append("AND p_product_ext.source_type = :sourceType \n");
    		paramsMap.put("sourceType", productRequest.getSourceType());
    	}
    	if(productRequest.getSupplierId() != null) {
    		paramsSQL.append("AND s_supplier.id = :supplierId \n");
    		paramsMap.put("supplierId", productRequest.getSupplierId());
    	}
    	if(StrUtil.isNotBlank(productRequest.getCategoryIds())) {
    		List<String> categoryIdLists = StrUtil.split(productRequest.getCategoryIds(), ',');
    		if(categoryIdLists != null && !categoryIdLists.isEmpty()) {
    			paramsSQL.append("AND p_product_ext.category_id in (:categoryIds) \n");
        		paramsMap.put("categoryIds", categoryIdLists);
    		}
    	}
    	if(productRequest.getCategoryId() != null) {
    		paramsSQL.append("AND p_product_ext.category_id = :categoryId \n");
    		paramsMap.put("categoryId", productRequest.getCategoryId());
    	}
    	
    	if(productRequest.getBegainTime() != null && productRequest.getEndTime() != null) {
    		paramsSQL.append("AND p_product.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
    		paramsMap.put("begainTime", productRequest.getBegainTime());
    		paramsMap.put("endTime", productRequest.getEndTime());
    	}
    	
    	String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
    	String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
    	
    	return jdbcTemplate.queryForList(contentSQL + paramsSQL + orderSQL + pageSQL, paramsMap);
    }
    
    private BigDecimal handleCrawlerPrice(String sourcePrice , BigDecimal rate) {
    	if(StrUtil.isBlank(sourcePrice)) return BigDecimal.ZERO;
    	
    	List<String> sourcePriceSplits = StrUtil.splitTrim(sourcePrice, "-");
    	
    	if(sourcePriceSplits.size() == 0) return BigDecimal.ZERO;
    	
    	for (String _sourcePrice : sourcePriceSplits) {
			if(StrUtil.isNotBlank(_sourcePrice)) {
				_sourcePrice = StrUtil.removePrefix(_sourcePrice, "￥");
				_sourcePrice = StrUtil.removePrefix(_sourcePrice, "US $");
				return Convert.toBigDecimal(_sourcePrice).multiply(rate.setScale(0, BigDecimal.ROUND_DOWN));
			}
		}
    	return BigDecimal.ZERO;
    }
    
    private String findSourceType(String productUrl) {
    	if(StrUtil.containsIgnoreCase(productUrl, "tmall")) return StatusConstant.SOURCE_TYPE_TMALL;
    	if(StrUtil.containsIgnoreCase(productUrl, "taobao")) return StatusConstant.SOURCE_TYPE_TAOBAO;
    	if(StrUtil.containsIgnoreCase(productUrl, "1688")) return StatusConstant.SOURCE_TYPE_1688;
    	if(StrUtil.containsIgnoreCase(productUrl, "aliexpress")) return StatusConstant.SOURCE_TYPE_ALIPRESS;
    	if(StrUtil.containsIgnoreCase(productUrl, "yangkeduo")) return StatusConstant.SOURCE_TYPE_PDD;
    	return "other" ; 
    }
    
    /*************************************************************************************************************99api*****************************************************************************************/
    
    @Transactional
    public Product createTaobaoTmallProduct99api(ProductTmallAndTaobao _product , String productUrl) {
    	
    	Preconditions.checkArgument(_product != null, "参数信息不存在...");
    	
    	log.info("Product...");
    	Product product = new Product();
    	product.setTitle(ZHConverter.convert(_product.getItem().getTitle(), ZHConverter.TRADITIONAL));
    	product.setImage(create99ProductImage(_product.getItem().getImages()));
    	product.setDescription("");
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_IN);
    	productRepository.save(product);
    	
    	log.info("ProductExt...");
    	ProductExt productExt = new ProductExt();
    	productExt.setProductId(product.getId());
    	productExt.setComments("crawler-" + LocalDateTime.now(ZoneId.of("+8")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    	productExt.setSourceType(findSourceType(productUrl));
    	productExt.setSupplierId(find99SupplierId(_product));
    	productExt.setSourceId(_product.getItem().getItemId());
    	productExt.setSourceTitle(_product.getItem().getTitle());
    	productExt.setSourceUrl(productUrl);
    	productExt.setProductDetails(create99ProductDetails(_product.getItem().getDescImgs()));
    	String carouselImages = create99ProductCarouselImages(_product.getItem().getImages());
    	productExt.setCarouselImages(product.getImage() + (StrUtil.isEmpty(carouselImages) ? "" : (";" + carouselImages)));
    	
    	//20200715
    	productExt.setProductProps(create99ProductProps(_product.getItem().getGroupProps()));
    	productExtRepository.save(productExt);
    	
    	log.info("ProductAttribute...");
    	List<ProductAttribute> productAttributes = new ArrayList<>();
    	List<ProductProp> props = _product.getItem().getProps();
    	for (int i = 0 ; i < props.size() ; i++) {
    		ProductAttribute productAttribute = new ProductAttribute();
    		productAttribute.setName(ZHConverter.convert(props.get(i).getName(), ZHConverter.TRADITIONAL));
    		productAttribute.setPriority(i);
    		productAttribute.setProductId(product.getId());
    		productAttributeRepository.save(productAttribute);
    		
    		productAttributes.add(productAttribute);
    		
    		List<ProductPropValue> values = props.get(i).getValues();
    		for (int j = 0 ; j < values.size() ; j++) {
    			if(StrUtil.isNotBlank(values.get(j).getImage())) {
    				values.get(j).setImage(boheUtils.saveImage(values.get(j).getImage()));
    			}
    			ProductAttributeValue productAttributeValue = new ProductAttributeValue();
    			productAttributeValue.setPriority(j);
    			productAttributeValue.setProductAttributeId(productAttribute.getId());
    			productAttributeValue.setProductId(product.getId());
    			productAttributeValue.setValue(ZHConverter.convert(HtmlUtil.cleanHtmlTag(values.get(j).getName()).replace("-", "~").replace("/", "#"), ZHConverter.TRADITIONAL));
    			productAttributeValue.setImage(values.get(j).getImage());
    			
    			//20200715
    			productAttributeValue.setSourceValue(HtmlUtil.cleanHtmlTag(values.get(j).getName()));
    			productAttributeValueRepository.save(productAttributeValue);
    			values.get(j).setYikomeId(productAttributeValue.getId());
			}
		}
    	
    	log.info("ProductSKU...");
    	List<ProductItemSku> skus = _product.getItem().getSku();
    	List<ProductSku> productSkus = new ArrayList<>();
    	for (ProductItemSku productItemSku : skus) {
    		if(StrUtil.isNotBlank(StrUtil.unWrap(productItemSku.getPropPath(), '"'))) {
    			
    			BigDecimal purchasePrice = Convert.toBigDecimal(productItemSku.getPrice() , BigDecimal.ZERO);
    			
    			ProductSku productSku = new ProductSku();
        		productSkuRepository.save(productSku);
        		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
        		productSku.setName(ZHConverter.convert(HtmlUtil.cleanHtmlTag(productItemSku.getSkuName()).replace("-", "~").replace("/", "#"), ZHConverter.TRADITIONAL));
        		productSku.setProductId(product.getId());
        		productSku.setInventory(productItemSku.getQuantity() > 0 ? Integer.valueOf(99999) : 0);
        		productSku.setProductAttributeValueId1(find99ProductAttributeValueId1(productItemSku.getPropPath(), _product.getItem().getProps()));
        		if(productAttributes.size() > 1) {
        			productSku.setProductAttributeValueId2(find99ProductAttributeValueId2(productItemSku.getPropPath(), _product.getItem().getProps()));
        		}
        		productSku.setImage(boheUtils.saveImage(productItemSku.getImage()));

        		productSku.setPurchasePrice(purchasePrice);
        		productSku.setPrice(findTriplePrice(productSku.getPurchasePrice()));
        		
        		//20200715
        		productSku.setSkuId(productItemSku.getSkuId());
        		productSkuRepository.save(productSku);
        		productSkus.add(productSku);
    		}
		}
    	
    	BigDecimal minPrice = BigDecimal.valueOf(Double.MAX_VALUE);
    	BigDecimal maxPrice = BigDecimal.ZERO;
    	for (ProductSku productSku : productSkus) {
			if(minPrice.compareTo(productSku.getPrice()) >= 0) minPrice = productSku.getPrice() ; 
			if(productSku.getPrice().compareTo(maxPrice) >= 0) maxPrice = productSku.getPrice() ; 
		}
    	
    	product.setPrice(minPrice);
    	product.setOriginalPrice(maxPrice.multiply(originalMultiplicand).setScale(0, BigDecimal.ROUND_HALF_DOWN));
    	
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_SUCCESS);
    	productRepository.save(product);
    	
    	return product ; 
    }
    
    //20200715
    private String create99ProductProps(List<Map<String, List<Map<String, String>>>> groupProps) {
    	
    	List<Map<String, String>> list = groupProps.get(0).get("基本信息");
		
		StringBuilder tableHtml = new StringBuilder("<table class='table table-bordered'><tbody>") ;
		for (Map<String, String> tmp : list) {
			for (Map.Entry<String, String> _tmp : tmp.entrySet()) {
				tableHtml.append(StrUtil.format("<tr><td><span>{}</span></td><td>{}</td></tr>", ZHConverter.convert(_tmp.getKey(), ZHConverter.TRADITIONAL), ZHConverter.convert(_tmp.getValue(), ZHConverter.TRADITIONAL)));
			}
		}
		tableHtml.append("</tbody></table>");
		
		return tableHtml.toString();
    }
    
    private String createykProductProps(List<ProductPddProperty> properties) {
    	
		StringBuilder tableHtml = new StringBuilder("<table class='table table-bordered'><tbody>") ;
		for (ProductPddProperty temp : properties) {
			tableHtml.append(StrUtil.format("<tr><td><span>{}</span></td><td>{}</td></tr>", temp.getKey(), CollUtil.join(temp.getValues(), ",")));
		}
		tableHtml.append("</tbody></table>");
		
		return tableHtml.toString();
    }
    
    private String create99ProductImage(List<String> images) {
    	if(images == null || images.isEmpty()) return "" ; 
    	return boheUtils.saveImage(images.get(0));
    }
    
    private Long find99SupplierId(ProductTmallAndTaobao productTmallAndTaobao) {
    	
    	Supplier supplier = supplierRepository.findSupplierByShopId(productTmallAndTaobao.getSeller().getShopId());
    	if(supplier == null) {
    		supplier = new Supplier();
    		supplier.setAddress(productTmallAndTaobao.getItem().getLocation());
    		supplier.setShopId(productTmallAndTaobao.getSeller().getShopId());
    		supplier.setShopName(productTmallAndTaobao.getSeller().getShopName());
    		supplierRepository.save(supplier);
    	}
    	return supplier.getId();
    }
    
    private Long findykSupplierId(ProductPdd product) {
    	
    	Supplier supplier = supplierRepository.findSupplierByShopId(product.getMall_id());
    	if(supplier == null) {
    		supplier = new Supplier();
    		supplier.setShopId(product.getMall_id());
    		supplier.setShopName(product.getMall_name());
    		supplierRepository.save(supplier);
    	}
    	return supplier.getId();
    }
    
    private Long find168899SupplierId(Product1688 product1688) {
    	
    	Supplier supplier = supplierRepository.findSupplierByShopId(product1688.getSellerUserId());
    	if(supplier == null) {
    		supplier = new Supplier();
    		supplier.setAddress(product1688.getLocation());
    		supplier.setShopId(product1688.getSellerUserId());
    		supplier.setShopName(product1688.getSellerName());
    		supplierRepository.save(supplier);
    	}
    	return supplier.getId();
    }
    
    private String create99ProductDetails(List<String> descImgs) {
    	if(CollUtil.isEmpty(descImgs)) return "" ;
    	StringBuilder details = new StringBuilder(); 
    	for (String sourceImageUrl : descImgs) {
    		if (StrUtil.containsAny(sourceImageUrl, "alicdn.com", "jpeg")) {
    			String imgUrl = boheUtils.saveImage(sourceImageUrl);
    			if(StrUtil.isNotBlank(imgUrl))details.append("<p><img src=\"").append(imgUrl).append("\"/></p>");
            }
		}
        return details.toString();
    }
    private String create99ProductCarouselImages(List<String> images) {
    	if(images == null || images.isEmpty()) return "" ; 
    	StringBuilder carouselImages = new StringBuilder();
    	for (String img : images) {
    		try {
    			String imgUrl = boheUtils.saveImage(img);
        		if(StrUtil.isNotBlank(imgUrl))carouselImages.append(";").append(imgUrl);
    		}catch (Exception e) {
				log.error(e.getMessage());
			}
		}
    	carouselImages.deleteCharAt(0);
    	return carouselImages.toString();
    }
    
    private Long find99ProductAttributeValueId1(String propPath , List<ProductProp> productProps) {
    	
    	for (ProductProp productProp : productProps) {
    		List<ProductPropValue> values = productProp.getValues();
    		for (ProductPropValue productPropValue : values) {
				if(StrUtil.startWith(propPath, productProp.getPid() + ":" + productPropValue.getVid())) {
					return productPropValue.getYikomeId();
				}
			}
		}
		return null ;
    }
    
    private Long find99ProductAttributeValueId2(String propPath , List<ProductProp> productProps) {
    	
    	for (ProductProp productProp : productProps) {
    		List<ProductPropValue> values = productProp.getValues();
    		for (ProductPropValue productPropValue : values) {
				if(StrUtil.endWith(propPath, productProp.getPid() + ":" + productPropValue.getVid())) {
					return productPropValue.getYikomeId();
				}
			}
		}
		return null ;
    }
    
    @Transactional
    public Product create1688Product99api(Product1688 _product , String productUrl) {
    	
    	Preconditions.checkArgument(_product != null, "参数信息不存在...");
    	Product product = new Product();
    	product.setTitle(ZHConverter.convert(_product.getTitle(), ZHConverter.TRADITIONAL));
    	product.setImage(create99ProductImage(_product.getImages()));
    	product.setDescription("");
    	product.setPrice(handleCrawlerPrice(_product.getDisplayPrice(), BigDecimal.valueOf(1)));
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_IN);
    	productRepository.save(product);
    	
    	log.info("ProductExt...");
    	ProductExt productExt = new ProductExt();
    	productExt.setProductId(product.getId());
    	productExt.setComments("crawler-" + LocalDateTime.now(ZoneId.of("+8")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    	productExt.setSourceType(findSourceType(productUrl));
    	productExt.setSupplierId(find168899SupplierId(_product));
    	productExt.setSourceId(_product.getItemId());
    	productExt.setSourceTitle(_product.getTitle());
    	productExt.setSourceUrl(productUrl);
    	productExt.setProductDetails(create99ProductDetails(_product.getDescImgs()));
    	String carouselImages = create99ProductCarouselImages(_product.getImages());
    	productExt.setCarouselImages(product.getImage() + (StrUtil.isEmpty(carouselImages) ? "" : (";" + carouselImages)));
    	
    	//20200715
    	productExt.setProductProps(find168899Props(_product.getAttributes()));
    	productExtRepository.save(productExt);
    	
    	// 存在SKU不存在的情况,此时需要手动构建一个SKU出来
    	if(_product.getSkuProps() == null || _product.getSkuProps().isEmpty()) {
    		Product1688SkuProps product1688SkuProps = new Product1688SkuProps();
    		product1688SkuProps.setProp("规格");
    		
    		Product1688SkuPropValue product1688SkuPropValue = new Product1688SkuPropValue();
    		product1688SkuPropValue.setImage(product.getImage());
    		product1688SkuPropValue.setImageUrl(product.getImage());
    		product1688SkuPropValue.setName("同图片");
    		
    		product1688SkuProps.setValue(ImmutableList.of(product1688SkuPropValue));
    		
    		_product.setSkuProps(ImmutableList.of(product1688SkuProps));
    	}
    	
    	log.info("ProductAttribute...");
    	List<Product1688SkuProps> props = _product.getSkuProps();
    	for (int i = 0 ; i < props.size() ; i++) {
    		ProductAttribute productAttribute = new ProductAttribute();
    		productAttribute.setName(ZHConverter.convert(props.get(i).getProp(), ZHConverter.TRADITIONAL));
    		productAttribute.setPriority(i);
    		productAttribute.setProductId(product.getId());
    		productAttributeRepository.save(productAttribute);
    		
    		List<Product1688SkuPropValue> values = props.get(i).getValue();
    		for (int j = 0 ; j < values.size() ; j++) {
    			if(StrUtil.isNotBlank(values.get(j).getImageUrl())) {
    				values.get(j).setImage(boheUtils.saveImage(values.get(j).getImageUrl()));
    			}
    			ProductAttributeValue productAttributeValue = new ProductAttributeValue();
    			productAttributeValue.setPriority(j);
    			productAttributeValue.setProductAttributeId(productAttribute.getId());
    			productAttributeValue.setProductId(product.getId());
    			productAttributeValue.setValue(ZHConverter.convert(HtmlUtil.cleanHtmlTag(values.get(j).getName()).replace("-", "~").replace("/", "#"), ZHConverter.TRADITIONAL));
    			productAttributeValue.setImage(values.get(j).getImageUrl());
    			
    			//20200715
    			productAttributeValue.setSourceValue(HtmlUtil.cleanHtmlTag(values.get(j).getName()));
    			productAttributeValueRepository.save(productAttributeValue);
    			values.get(j).setYikomeId(productAttributeValue.getId());
			}
		}
    	
    	log.info("ProductSKU...");
    	List<ProductSku> productSkus = new ArrayList<>();
    	List<Product1688SkuPropValue> values = props.get(0).getValue();
		for (int j = 0 ; j < values.size() ; j++) {
			String propImage = values.get(j).getImage();
			String propName = values.get(j).getName();
			
			if(props.size() > 2) {
				throw new RuntimeException("不支持3个及以上属性...");
			}else if(props.size() == 2) {
				List<Product1688SkuPropValue> value2s = props.get(1).getValue();
				for (int jj = 0 ; jj < value2s.size() ; jj++) {
					String propName2 = value2s.get(jj).getName();
					
					ProductSku productSku = new ProductSku();
	        		productSkuRepository.save(productSku);
	        		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
	        		productSku.setName(ZHConverter.convert(propName + "-" + propName2, ZHConverter.TRADITIONAL));
	        		productSku.setProductId(product.getId());
	        		productSku.setInventory(Integer.valueOf(99999));
	        		productSku.setProductAttributeValueId1(values.get(j).getYikomeId());
	        		productSku.setProductAttributeValueId2(value2s.get(jj).getYikomeId());
	        		productSku.setImage(propImage);
	        		productSku.setPrice(findTriplePrice(product.getPrice()));
	        		productSku.setPurchasePrice(product.getPrice());
	        		
	        		//20200715
	        		productSku.setSkuId(find168899apiSkuId(_product.getSkuMap() , propName , propName2));
	        		productSkuRepository.save(productSku);
	        		productSkus.add(productSku);
				}
			}else {
				ProductSku productSku = new ProductSku();
        		productSkuRepository.save(productSku);
        		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
        		productSku.setName(ZHConverter.convert(propName, ZHConverter.TRADITIONAL));
        		productSku.setProductId(product.getId());
        		productSku.setInventory(Integer.valueOf(99999));
        		productSku.setProductAttributeValueId1(values.get(j).getYikomeId());
        		productSku.setImage(propImage);
        		productSku.setPrice(findTriplePrice(product.getPrice()));
        		productSku.setPurchasePrice(product.getPrice());
        		productSkuRepository.save(productSku);
        		productSkus.add(productSku);
			}
		}
		
		BigDecimal minPrice = BigDecimal.valueOf(Double.MAX_VALUE);
    	BigDecimal maxPrice = BigDecimal.ZERO;
    	for (ProductSku productSku : productSkus) {
			if(minPrice.compareTo(productSku.getPrice()) >= 0) minPrice = productSku.getPrice() ; 
			if(productSku.getPrice().compareTo(maxPrice) >= 0) maxPrice = productSku.getPrice() ; 
		}
    	product.setPrice(minPrice);
    	product.setOriginalPrice(maxPrice.multiply(originalMultiplicand).setScale(0, BigDecimal.ROUND_HALF_DOWN));
		
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_SUCCESS);
    	productRepository.save(product);
    	
    	return product ; 
    }
    
    private String find168899Props(List<String> attributes) {
    	
    	if(CollUtil.isEmpty(attributes)) return "" ;
    	
		StringBuilder tableHtml = new StringBuilder("<table class='table table-bordered'><tbody>") ;
		for (String tmp : attributes) {
			//ZHConverter.convert(_tmp.getKey(), ZHConverter.TRADITIONAL)
			String[] array = StrUtil.splitToArray(tmp, ':', 2);
			for (int i = 0; i < array.length; i++) {
				array[i] = ZHConverter.convert(array[i], ZHConverter.TRADITIONAL);
			}
			tableHtml.append(StrUtil.format("<tr><td><span>{}</span></td><td>{}</td></tr>", array));
		}
		tableHtml.append("</tbody></table>");
		
		return tableHtml.toString();
    }
    
    private String find168899apiSkuId(Map<String , Map<String , Object>> skuMaps , String propName1 , String propName2) {
    	
    	for (Map.Entry<String , Map<String , Object>> skuMap : skuMaps.entrySet()) {
    		String key = skuMap.getKey();
    		if(StrUtil.containsIgnoreCase(key, propName1) && StrUtil.containsIgnoreCase(key, propName2)) {
    			Map<String , Object> value = skuMap.getValue();
    			return value.get("skuId").toString();
    		}
		}
    	
    	return "" ;
    }
    
    
    private BigDecimal findTriplePrice(BigDecimal price) {
    	return price; 
    }
    
//    private BigDecimal findTriplePrice(BigDecimal price) {
//    	
//    	for (Triple<BigDecimal, BigDecimal, BigDecimal> triple : priceTripes) {
//			
//    		if(triple.getLeft().compareTo(price) <= 0 && price.compareTo(triple.getMiddle()) <= 0) {
//    			return price.multiply(triple.getRight()).setScale(0, BigDecimal.ROUND_DOWN);
//    		}
//		}
//    	return BigDecimal.ZERO; 
//    	
//    }
    
    public List<Product> findAllProduct(String sourceType){
    	String contentSQL = "SELECT\r\n" + 
			    			"	p_product.*\r\n" + 
			    			"FROM p_product\r\n" + 
			    			"	LEFT JOIN p_product_ext ON (p_product.id = p_product_ext.product_id)\r\n" + 
			    			"WHERE p_product.yn = 1\r\n" + 
			    			"AND p_product_ext.source_type = :sourceType ";
    	return jdbcTemplate.query(contentSQL, ImmutableMap.of("sourceType", sourceType), new BeanPropertyRowMapper<>(Product.class));
    }
    
    @Transactional
    public void update1688Price(Product product) throws InterruptedException {
    	
    	ProductExt productExt = productExtRepository.findByProductId(product.getId());
    	
    	if(!StrUtil.equalsIgnoreCase("1688", productExt.getSourceType())) return ; 
    	
		TimeUnit.SECONDS.sleep(2);
		String crawlerJson = HttpRequest.get(StrUtil.format("{}&apikey={}", "https://api03.6bqb.com/alibaba/detail?itemid=" + productExt.getSourceId() , "79C33118FD22C5EC6871EB0DF1CC03EC"))
				.charset(Charset.forName("UTF-8"))
				.header("Accept-Encoding", "gzip")
				.execute()
				.body();
		
		log.info("crawlerJson : {}" , crawlerJson);
		
		Preconditions.checkArgument(StrUtil.equalsIgnoreCase(StrUtil.unWrap(JSONUtils.getJsonNodeString(crawlerJson, "retcode"), '"'), "0000"), "API请求失败...");
		
		Product1688 crawlerProduct = JSONUtils.parseJson(JSONUtils.getJsonNodeString(crawlerJson, "data"), Product1688.class);
		
		BigDecimal purchasePrice = handleCrawlerPrice(crawlerProduct.getDisplayPrice(), BigDecimal.valueOf(1)) ; 
		
		List<ProductSku> productSkus = productSkuRepository.findAllByProductId(product.getId());
		for (ProductSku productSku : productSkus) {
			productSku.setPurchasePrice(purchasePrice);
			productSku.setPrice(findTriplePrice(purchasePrice));
		}
		productSkuRepository.saveAll(productSkus);
		
		BigDecimal minPrice = BigDecimal.valueOf(Double.MAX_VALUE);
    	BigDecimal maxPrice = BigDecimal.ZERO;
    	for (ProductSku productSku : productSkus) {
			if(minPrice.compareTo(productSku.getPrice()) >= 0) minPrice = productSku.getPrice() ; 
			if(productSku.getPrice().compareTo(maxPrice) >= 0) maxPrice = productSku.getPrice() ; 
		}
    	product.setPrice(minPrice);
    	product.setOriginalPrice(maxPrice.multiply(originalMultiplicand).setScale(0, BigDecimal.ROUND_HALF_DOWN));
		
    	productRepository.save(product);
    }
    
    
    /*************************************************************************************************************99api*****************************************************************************************/
    
    
    
    @Transactional
    public Product crawlerykpddProduct(ProductPdd _product , String productUrl) {
    	
    	Preconditions.checkArgument(_product != null, "参数信息不存在...");
    	
    	log.info("Product...");
    	Product product = new Product();
    	product.setTitle(_product.getGoods_name());
    	product.setImage(create99ProductImage(_product.getMain_figures()));
    	product.setDescription("");
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_IN);
    	productRepository.save(product);
    	
    	log.info("ProductExt...");
    	ProductExt productExt = new ProductExt();
    	productExt.setProductId(product.getId());
    	productExt.setComments("crawler-" + LocalDateTime.now(ZoneId.of("+8")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    	productExt.setSourceType(findSourceType(productUrl));
    	productExt.setSupplierId(findykSupplierId(_product));
    	productExt.setSourceId(_product.getGoods_id());
    	productExt.setSourceTitle(_product.getGoods_name());
    	productExt.setSourceUrl(productUrl);
    	productExt.setProductDetails(create99ProductDetails(_product.getDetail_figuers()));
    	String carouselImages = create99ProductCarouselImages(_product.getMain_figures());
    	productExt.setCarouselImages(carouselImages);
    	
    	//20200715
    	productExt.setProductProps(createykProductProps(_product.getProperties()));
    	productExtRepository.save(productExt);
    	
    	//用于ID的取值
    	Map<String, ProductAttributeValue> attributeValueMap = new HashMap<>();
    	
    	log.info("ProductAttribute...");
    	List<ProductAttribute> productAttributes = new ArrayList<>();
    	List<ProductPddAttr> props = _product.getSku_big_types();
    	
    	if(props.size() > 2) {
			throw new RuntimeException("不支持3个及以上属性...");
		}
    	
    	for (int i = 0 ; i < props.size() ; i++) {
    		ProductAttribute productAttribute = new ProductAttribute();
    		productAttribute.setName(props.get(i).getSpec_key());
    		productAttribute.setPriority(i);
    		productAttribute.setProductId(product.getId());
    		productAttributeRepository.save(productAttribute);
    		
    		productAttributes.add(productAttribute);
    		
    		List<Map<String, String>> values = props.get(i).getSpec_values();
    		for (int j = 0 ; j < values.size() ; j++) {
    			
    			for(Map.Entry<String, String> temp : values.get(j).entrySet()) {
    				
    				ProductAttributeValue productAttributeValue = new ProductAttributeValue();
        			productAttributeValue.setPriority(j);
        			productAttributeValue.setProductAttributeId(productAttribute.getId());
        			productAttributeValue.setProductId(product.getId());
        			productAttributeValue.setValue(HtmlUtil.cleanHtmlTag(temp.getValue()).replace("-", "~").replace("/", "#"));
        			
        			//20200715
        			productAttributeValue.setSourceValue(HtmlUtil.cleanHtmlTag(temp.getValue()));
        			productAttributeValueRepository.save(productAttributeValue);
        			
        			attributeValueMap.put(temp.getKey(), productAttributeValue);
    			}
			}
		}
    	
    	log.info("ProductSKU...");
    	List<ProductPddSku> skus = _product.getSku_products();
    	List<ProductSku> productSkus = new ArrayList<>();
    	for (ProductPddSku productItemSku : skus) {
    		
    		ProductSku productSku = new ProductSku();
    		productSkuRepository.save(productSku);
    		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
    		productSku.setProductId(product.getId());
    		productSku.setInventory(Integer.valueOf(99999));
    		
    		List<String> attrValueTempValus = new ArrayList<>();
    		List<String> splits = StrUtil.split(productItemSku.getSkus(), ',');
    		for (String split : splits) {
    			attrValueTempValus.add(attributeValueMap.get(split).getValue());
			}
    		
    		productSku.setName(HtmlUtil.cleanHtmlTag(CollUtil.join(attrValueTempValus, "/")).replace("-", "~").replace("/", "#"));
    		productSku.setProductAttributeValueId1(attributeValueMap.get(splits.get(0)).getId());
    		if(productAttributes.size() > 1) {
    			productSku.setProductAttributeValueId2(attributeValueMap.get(splits.get(1)).getId());
    		}
    		
    		
    		productSku.setImage(boheUtils.saveImage(productItemSku.getSku_url()));

    		productSku.setPurchasePrice(productItemSku.getGroup_price());
    		productSku.setPrice(findTriplePrice(productSku.getPurchasePrice()));
    		
    		//20200715
    		productSku.setSkuId(productItemSku.getSkus());
    		productSkuRepository.save(productSku);
    		productSkus.add(productSku);
		}
    	
    	BigDecimal minPrice = BigDecimal.valueOf(Double.MAX_VALUE);
    	BigDecimal maxPrice = BigDecimal.ZERO;
    	for (ProductSku productSku : productSkus) {
			if(minPrice.compareTo(productSku.getPrice()) >= 0) minPrice = productSku.getPrice() ; 
			if(productSku.getPrice().compareTo(maxPrice) >= 0) maxPrice = productSku.getPrice() ; 
		}
    	
    	product.setPrice(minPrice);
    	product.setOriginalPrice(maxPrice.multiply(originalMultiplicand).setScale(0, BigDecimal.ROUND_HALF_DOWN));
    	
    	product.setStatus(StatusConstant.PRODUCT_STATUS_CREATE_SUCCESS);
    	productRepository.save(product);
    	
    	return product ; 
    }    
    
    
    /**
     * 获取一个产品明细
     */
    public ProductEditDTO findProduct(Long productId) {
    	
    	ProductEditDTO productEditDTO = new ProductEditDTO();
    	Product product = productRepository.findByProductId(productId);
    	
    	BeanUtil.copyProperties(product, productEditDTO);
    	
    	Preconditions.checkArgument(product != null, "产品不存在...");
    	
    	ProductExtEditDTO productExtEditDTO = new ProductExtEditDTO();
    	ProductExt productExt = productExtRepository.findByProductId(productId);
    	BeanUtil.copyProperties(productExt, productExtEditDTO);
    	productEditDTO.setProductExtEditDTO(productExtEditDTO);
    	
    	
    	List<ProductAttributeEditDTO> productAttributeEditDTOs = new ArrayList<>();
    	List<ProductAttribute> productAttributes = productAttributeRepository.findByProductId(productId);
    	for (ProductAttribute productAttribute : productAttributes) {
    		ProductAttributeEditDTO productAttributeEditDTO = new ProductAttributeEditDTO();
    		BeanUtil.copyProperties(productAttribute, productAttributeEditDTO);
    		List<ProductAttributeValue> productAttributeValues = productAttributeValueRepository.findAllProductAttributeValue(productId, productAttribute.getId());
    		
    		List<ProductAttributeValueEditDTO> productAttributeValueEditDTOs = new ArrayList<>();
    		for (ProductAttributeValue productAttributeValue : productAttributeValues) {
    			ProductAttributeValueEditDTO productAttributeValueEditDTO = new ProductAttributeValueEditDTO();
    			BeanUtil.copyProperties(productAttributeValue, productAttributeValueEditDTO);
    			productAttributeValueEditDTOs.add(productAttributeValueEditDTO);
			}
    		
    		productAttributeEditDTO.setProductAttributeValueEditDTOs(productAttributeValueEditDTOs);
    		productAttributeEditDTOs.add(productAttributeEditDTO);
		}
    	productEditDTO.setProductAttributeEditDTOs(productAttributeEditDTOs);
    	
    	List<ProductSkuEditDTO> productSkuEditDTOs = new ArrayList<>();
    	List<ProductSku> productSkus = productSkuRepository.findAllByProductId(productId);
    	for (ProductSku productSku : productSkus) {
    		ProductSkuEditDTO productSkuEditDTO = new ProductSkuEditDTO();
    		BeanUtil.copyProperties(productSku, productSkuEditDTO);
    		productSkuEditDTOs.add(productSkuEditDTO);
		}
    	productEditDTO.setProductSkuEditDTOs(productSkuEditDTOs);
    	
    	return productEditDTO ; 
    }

    /**
     * edit产品
     * 1. 产品基本信息及扩展信息,直接更新完成即可
     * 2. 产品SKU采取更新策略,然后,将不存在列表中的SKU全部置为yn = 0 或者直接删除
     * 3. 规格属性值,采取更新策略,然后将不存在列表中的SKU全部置为yn = 0 或者直接删除
     * @param productEditDTO
     */
    @Transactional
	public void editProduct(ProductEditDTO productEditDTO) {

    	Product product = productRepository.findByProductId(productEditDTO.getId());
    	BeanUtil.copyProperties(productEditDTO, product , "id" , "yn" , "status");
    	productRepository.save(product);
    	
    	ProductExt productExt = productExtRepository.findByProductId(productEditDTO.getId());
    	BeanUtil.copyProperties(productEditDTO.getProductExtEditDTO(), productExt , "id" , "yn" , "productId" , "upAt" , "downAt");
    	productExtRepository.save(productExt);
    	
    	//属性不存在新增,但是存在修改和删除....
    	List<ProductAttribute> productAttributes = productAttributeRepository.findByProductId(productEditDTO.getId());
    	for (ProductAttribute productAttribute : productAttributes) {
			
    		ProductAttributeEditDTO attributeEditDTO = findProductAttributeEditDTO(productEditDTO.getProductAttributeEditDTOs(), productAttribute.getId());
    		if(attributeEditDTO == null) {
    			productAttributeRepository.delete(productAttribute);
    			productAttributeValueRepository.deleteByProductIdAndProductAttributeId(productEditDTO.getId() , productAttribute.getId());
    		}else {
    			productAttribute.setName(attributeEditDTO.getName());
    			productAttributeRepository.save(productAttribute);
    		
	    		List<ProductAttributeValue> productAttributeValues = productAttributeValueRepository.findAllProductAttributeValue(productEditDTO.getId(), productAttribute.getId());
	    		for (ProductAttributeValue productAttributeValue : productAttributeValues) {
	    			
	    			ProductAttributeValueEditDTO attributeValueEditDTO = findProductAttributeValueEditDTO(attributeEditDTO.getProductAttributeValueEditDTOs(), productAttributeValue.getId());
	    			if(attributeValueEditDTO == null) {
	    				productAttributeValueRepository.delete(productAttributeValue);
	    			}else {
	    				productAttributeValue.setValue(attributeValueEditDTO.getValue());
	    				productAttributeValueRepository.save(productAttributeValue);
	    			}
	    		}
    		}
		}
    	
    	BigDecimal productPrice = BigDecimal.valueOf(99999);
    	//SKU不存在新增,但是存在修改和删除
    	List<ProductSku> productSkus = productSkuRepository.findAllByProductId(productEditDTO.getId());
    	for (ProductSku productSku : productSkus) {
    		ProductSkuEditDTO productSkuEditDTO = findProductSkuEditDTO(productEditDTO.getProductSkuEditDTOs(), productSku.getId());
    		if(productSkuEditDTO == null) {
    			productSkuRepository.delete(productSku);
    		}else {
    			productSku.setImage(productSkuEditDTO.getImage());
    			productSku.setInventory(productSkuEditDTO.getInventory());
    			productSku.setName(productSkuEditDTO.getName());
    			productSku.setPrice(productSkuEditDTO.getPrice());
    			productSku.setPurchasePrice(productSkuEditDTO.getPurchasePrice());
    			productSkuRepository.save(productSku);
    			
    			if(productSku.getInventory() > 0 && productPrice.compareTo(productSku.getPrice()) > 0) productPrice = productSku.getPrice() ;
    		}
		}
    	
    	product.setPrice(productPrice);
    	productRepository.save(product);
	}
    
    //TODO
    @Transactional
	public void copyProduct(Long productId) {
    	Product sourceProduct = productRepository.findByProductId(productId);
    	Preconditions.checkArgument(sourceProduct != null, "商品不存在...");
    	
    	Product newProduct = new Product();
    	BeanUtils.copyProperties(sourceProduct, newProduct, "id");
    	productRepository.save(newProduct);
    	
    	Map<Long, Long> attributeValueIdMaps = new HashMap<>();
    	
    	List<ProductAttribute> sourceAttributes = productAttributeRepository.findAllByProductId(sourceProduct.getId());
    	for (ProductAttribute sourceAttribute : sourceAttributes) {
    		ProductAttribute attribute = new ProductAttribute();
    		BeanUtils.copyProperties(sourceAttribute, attribute, "id", "productId");
    		attribute.setProductId(newProduct.getId());
    		productAttributeRepository.save(attribute);
    		
    		List<ProductAttributeValue> sourceAttributeValues = productAttributeValueRepository.findAllProductAttributeValue(sourceProduct.getId(), sourceAttribute.getId());
    		for (ProductAttributeValue sourceAttributeValue : sourceAttributeValues) {
				
    			ProductAttributeValue productAttributeValue = new ProductAttributeValue();
    			BeanUtils.copyProperties(sourceAttributeValue, productAttributeValue, "id", "productAttributeId", "productId");
    			productAttributeValue.setProductId(newProduct.getId());
    			productAttributeValue.setProductAttributeId(attribute.getId());
    			productAttributeValueRepository.save(productAttributeValue);
    			
    			attributeValueIdMaps.put(sourceAttributeValue.getId(), productAttributeValue.getId());
			}
		}
    	
    	ProductExt sourceProductExt = productExtRepository.findByProductId(sourceProduct.getId());
    	ProductExt newProductExt = new ProductExt();
    	BeanUtils.copyProperties(sourceProductExt, newProductExt, "id", "productId");
    	newProductExt.setProductId(newProduct.getId());
    	productExtRepository.save(newProductExt);
    	
    	List<ProductComments> sourceComments = productCommentsRepository.findByProductId(sourceProduct.getId());
    	for (ProductComments sourceComment : sourceComments) {
    		ProductComments newProductComments = new ProductComments();
    		BeanUtils.copyProperties(sourceComment, newProductComments, "id", "productId");
    		newProductComments.setProductId(newProduct.getId());
    		productCommentsRepository.save(newProductComments);
		}
    	
    	List<ProductSku> sourceProductSkus = productSkuRepository.findAllByProductId(sourceProduct.getId());
    	for (ProductSku sourceProductSku : sourceProductSkus) {
			
    		ProductSku newProductSku = new ProductSku();
    		BeanUtils.copyProperties(sourceProductSku, newProductSku, "id", "productId", "productAttributeValueId1", "productAttributeValueId2");
    		newProductSku.setProductId(newProduct.getId());
    		newProductSku.setProductAttributeValueId1(attributeValueIdMaps.get(sourceProductSku.getProductAttributeValueId1()));
    		newProductSku.setProductAttributeValueId2(attributeValueIdMaps.get(sourceProductSku.getProductAttributeValueId2()));
    		productSkuRepository.save(newProductSku);
		}
    	
    	List<ProductSet> sourceProductSets = productSetRepository.findByProductId(sourceProduct.getId());
    	for (ProductSet sourceProductSet : sourceProductSets) {
    		ProductSet newProductSet = new ProductSet();
    		BeanUtils.copyProperties(sourceProductSet, newProductSet, "id", "productId");
    		newProductSet.setProductId(newProduct.getId());
    		productSetRepository.save(newProductSet);
		}
    }
    
    @Transactional
	public void productSets(ProductSetEditDTO productSetEditDTO) {

    	Product product = productRepository.findByProductId(productSetEditDTO.getProductId());
    	Preconditions.checkArgument(product != null, "商品不存在...");
    	
    	List<ProductSet> exsits = productSetRepository.findByProductId(productSetEditDTO.getProductId());
    	if(exsits != null && !exsits.isEmpty()) {
    		productSetRepository.deleteAll(exsits);
    	}
    	
    	List<ProductSet> setParams = productSetEditDTO.getProductSets();
    	if(setParams == null || setParams.isEmpty()) return ;
    	
    	List<ProductSet> sets = new ArrayList<>();
    	for (ProductSet setParam : setParams) {
			
    		if(StrUtil.isNotBlank(setParam.getSetTitle())
    				&& setParam.getSetPrice() != null && setParam.getSetPrice().compareTo(BigDecimal.ZERO) > 0
    				&& setParam.getSetNumber() != null && setParam.getSetNumber().intValue() > 0) {
    			
    			ProductSet productSet = new ProductSet();
        		BeanUtils.copyProperties(setParam, productSet, "id", "productId");
        		productSet.setProductId(product.getId());
        		sets.add(productSet);
    		}
		}
    	
    	if(!sets.isEmpty()) {
    		productSetRepository.saveAll(sets);
    	}
	}
    
    public void addProductAttributeValue(ProductAttributeValueCreateDTO productAttributeValueCreateDTO) {
    	
    	ProductAttribute productAttribute = productAttributeRepository.findById(productAttributeValueCreateDTO.getAttributeId()).orElse(null);
    	
    	Preconditions.checkArgument(productAttribute != null, "产品属性不存在...");
    	Preconditions.checkArgument(productAttribute.getProductId().equals(productAttributeValueCreateDTO.getProductId()), "商品ID不匹配...");
    	
    	ProductAttributeValue productAttributeValue = new ProductAttributeValue();
    	productAttributeValue.setProductId(productAttribute.getProductId());
    	productAttributeValue.setProductAttributeId(productAttribute.getId());
    	productAttributeValue.setPriority(999);
    	productAttributeValue.setValue(productAttributeValueCreateDTO.getAttributeValueText());
    	
    	productAttributeValueRepository.save(productAttributeValue);
    	
    	//构建SKU合集
    	List<ProductAttribute> productAttributes = productAttributeRepository.findByProductId(productAttribute.getProductId());
    	if(productAttributes.size() == 1) {
    		ProductSku productSku = new ProductSku();
    		productSkuRepository.save(productSku);
    		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
    		productSku.setName(productAttributeValue.getValue());
    		productSku.setProductId(productAttributeValue.getProductId());
    		productSku.setInventory(9999);
    		productSku.setProductAttributeValueId1(productAttributeValue.getId());
    		productSku.setPrice(BigDecimal.ZERO);
    		productSku.setPurchasePrice(BigDecimal.ZERO);
    		productSkuRepository.save(productSku);
    	}else {
    		
    		for (int i = 0 ; i < productAttributes.size() ; i++) {
				
    			if(!productAttributes.get(i).getId().equals(productAttribute.getId())) {
    				
    				List<ProductAttributeValue> tmps = productAttributeValueRepository.findAllProductAttributeValue(productAttributes.get(i).getProductId(), productAttributes.get(i).getId());
    				
    				if(tmps == null || tmps.isEmpty()) {
    					ProductSku productSku = new ProductSku();
    		    		productSkuRepository.save(productSku);
    		    		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
    		    		productSku.setName(productAttributeValue.getValue());
    		    		productSku.setProductId(productAttributeValue.getProductId());
    		    		productSku.setInventory(9999);
    		    		productSku.setProductAttributeValueId1(productAttributeValue.getId());
    		    		productSku.setPrice(BigDecimal.ZERO);
    		    		productSku.setPurchasePrice(BigDecimal.ZERO);
    		    		productSkuRepository.save(productSku);
    				}else {
    					
    					for (ProductAttributeValue tmp : tmps) {
    						
    						ProductSku productSku = new ProductSku();
        		    		productSkuRepository.save(productSku);
        		    		productSku.setBarcode("68" + StrUtil.padPre(String.valueOf(productSku.getId()), 6, '0'));
        		    		productSku.setName(i == 0 ? tmp.getValue() + '-' + productAttributeValue.getValue() : productAttributeValue.getValue() + '-' +tmp.getValue());
        		    		productSku.setProductId(productAttributeValue.getProductId());
        		    		productSku.setInventory(9999);
        		    		productSku.setProductAttributeValueId1(i == 0 ? tmp.getId() : productAttributeValue.getId());
        		    		productSku.setProductAttributeValueId2(i == 0 ? productAttributeValue.getId() : tmp.getId());
        		    		productSku.setPrice(BigDecimal.ZERO);
        		    		productSku.setPurchasePrice(BigDecimal.ZERO);
        		    		productSkuRepository.save(productSku);
						}
    				}
    			}
			}
    	}
    }
    
    @Transactional
	public List<Product> batchEditProduct(List<ProductBatchEditDTO> productBatchEditDTOs) {
    	
    	List<Product> products = new ArrayList<>();
    	List<ProductExt> productExts = new ArrayList<>();
    	
    	for (ProductBatchEditDTO productBatchEditDTO : productBatchEditDTOs) {
			Product product = productRepository.findByProductId(productBatchEditDTO.getId());
			
			if(StrUtil.isNotBlank(productBatchEditDTO.getTitle()))product.setTitle(productBatchEditDTO.getTitle());
			if(StrUtil.isNotBlank(productBatchEditDTO.getDescription()))product.setDescription(productBatchEditDTO.getDescription());
			if(StrUtil.isNotBlank(productBatchEditDTO.getTags()))product.setTags(productBatchEditDTO.getTags());
			
			if(productBatchEditDTO.getPrice() != null) {
				
				product.setPrice(productBatchEditDTO.getPrice());
				List<ProductSku> productSkus = productSkuRepository.findAllByProductId(product.getId());
				for (ProductSku productSku : productSkus) {
					productSku.setPrice(product.getPrice());
				}
				productSkuRepository.saveAll(productSkus);
				
			}
			
			if(StrUtil.isNotBlank(productBatchEditDTO.getCoverImg())) {
				product.setCoverImg(productBatchEditDTO.getCoverImg());
			}
			if(productBatchEditDTO.getOriginalPrice() != null) product.setOriginalPrice(productBatchEditDTO.getOriginalPrice());
			products.add(product);
			
			
			ProductExt productExt = productExtRepository.findByProductId(product.getId());
			if(productBatchEditDTO.getCategoryId() != null) {
				productExt.setCategoryId(productBatchEditDTO.getCategoryId());
				productExts.add(productExt);
			}
			
		}
    	
    	productExtRepository.saveAll(productExts);
    	return productRepository.saveAll(products);
    	
    }
    
    private ProductAttributeEditDTO findProductAttributeEditDTO(List<ProductAttributeEditDTO> productAttributeEditDTOs , Long id) {
    	
    	if(productAttributeEditDTOs == null || productAttributeEditDTOs.isEmpty()) return null ; 
    	
    	for (ProductAttributeEditDTO productAttributeEditDTO : productAttributeEditDTOs) {
			if(productAttributeEditDTO.getId().equals(id)) return productAttributeEditDTO ; 
		}
    	
    	return null ; 
    }
    
    private ProductAttributeValueEditDTO findProductAttributeValueEditDTO(List<ProductAttributeValueEditDTO> productAttributeValueEditDTOs , Long id) {
    	
    	if(productAttributeValueEditDTOs == null || productAttributeValueEditDTOs.isEmpty()) return null ; 
    	
    	for (ProductAttributeValueEditDTO productAttributeValueEditDTO : productAttributeValueEditDTOs) {
			if(productAttributeValueEditDTO.getId().equals(id)) return productAttributeValueEditDTO ; 
		}
    	
    	return null ; 
    }
    
    private ProductSkuEditDTO findProductSkuEditDTO(List<ProductSkuEditDTO> productSkuEditDTOs , Long id) {
    	
    	if(productSkuEditDTOs == null || productSkuEditDTOs.isEmpty()) return null ; 
    	
    	for (ProductSkuEditDTO productSkuEditDTO : productSkuEditDTOs) {
			if(productSkuEditDTO.getId().equals(id)) return productSkuEditDTO ; 
		}
    	
    	return null ;
    }

    @Transactional
	public void deleteProduct(Long productId) {
		
    	Product product = productRepository.findByProductId(productId);
    	Preconditions.checkArgument(product != null, "商品不存在...");
		Preconditions.checkArgument(product.getStatus() != StatusConstant.PRODUCT_STATUS_ONLINE_UP, "上架产品不允许直接删除...");
    	
		productRepository.deleteById(productId);
		productExtRepository.deleteByProductId(productId);
		productAttributeRepository.deleteByProductId(productId);
		productAttributeValueRepository.deleteByProductId(productId);
		productSkuRepository.deleteByProductId(productId);
	}

    @Transactional
	public void upProduct(Long productId) {
		Product product = productRepository.findByProductId(productId);
		Preconditions.checkArgument(product != null, "信息不存在...");
		product.setStatus(StatusConstant.PRODUCT_STATUS_ONLINE_UP);
		productRepository.save(product);
		
		ProductExt productExt = productExtRepository.findByProductId(productId);
		productExt.setUpAt(Calendar.getInstance(TimeZone.getTimeZone("GMT+8")).getTime());
		productExtRepository.save(productExt);
		
		exchangeProductPrice(product , BigDecimal.valueOf(4.2));
	}

    @Transactional
	public void downProduct(Long productId) {
		Product product = productRepository.findByProductId(productId);
		Preconditions.checkArgument(product != null, "信息不存在...");
		product.setStatus(StatusConstant.PRODUCT_STATUS_ONLINE_DOWN);
		productRepository.save(product);
		
		ProductExt productExt = productExtRepository.findByProductId(productId);
		productExt.setDownAt(Calendar.getInstance(TimeZone.getTimeZone("GMT+8")).getTime());
		productExtRepository.save(productExt);
		
		List<CollectionProduct> collectionProducts = collectionProductRepository.findProductCollection(productId);
		collectionProductRepository.deleteAll(collectionProducts);
	}

    @Transactional
	public void exchangeProductPrice(BigDecimal exchangeRate) {

    	List<Product> products = productRepository.findStatusProduct(StatusConstant.PRODUCT_STATUS_ONLINE_UP);
    	
    	for (Product product : products) {
    		
    		exchangeProductPrice(product, exchangeRate);
		}
	}
    
    public void exchangeProductPrice(Product product, BigDecimal exchangeRate) {
    	
    	Integer number = 9 ;
    	
    	int remainder = product.getPrice().multiply(exchangeRate).setScale(0, BigDecimal.ROUND_DOWN).divideAndRemainder((BigDecimal.valueOf(10)))[1].intValue();
		if(remainder == 9) return;
		
		List<ProductSku> productSkus = productSkuRepository.findAllByProductId(product.getId());
		
		BigDecimal minPrice = BigDecimal.valueOf(Double.MAX_VALUE);
		BigDecimal maxPrice = BigDecimal.ZERO;
		
		for (ProductSku productSku : productSkus) {
			
			productSku.setPrice(getExchangePrice(exchangeRate, number, productSku.getPrice()));
			
			if(minPrice.compareTo(productSku.getPrice()) >= 0) minPrice = productSku.getPrice() ; 
			if(productSku.getPrice().compareTo(maxPrice) >= 0) maxPrice = productSku.getPrice() ; 
		}
		productSkuRepository.saveAll(productSkus);

		product.setPrice(minPrice);
		product.setOriginalPrice(maxPrice.multiply(originalMultiplicand).setScale(0, BigDecimal.ROUND_HALF_DOWN));
		
		productRepository.save(product);
	}
    
    public BigDecimal getExchangePrice(BigDecimal exchangeRate, Integer number , BigDecimal price) {

    	BigDecimal salePrice = price.multiply(exchangeRate) ;
		
    	//取值范围: 0-9.99
    	BigDecimal remainder = salePrice.divideAndRemainder((BigDecimal.valueOf(10)))[1].setScale(0, BigDecimal.ROUND_DOWN);
		
    	if(remainder.compareTo(BigDecimal.valueOf(5)) >= 0) {
    		remainder = remainder.subtract(BigDecimal.TEN);
    	}
    	
    	BigDecimal minPrice = (salePrice.setScale(0, BigDecimal.ROUND_DOWN).subtract(remainder).subtract(BigDecimal.ONE)).divide(exchangeRate, 3, BigDecimal.ROUND_UP);
    	BigDecimal maxPrice = (salePrice.setScale(0, BigDecimal.ROUND_UP).subtract(remainder).subtract(BigDecimal.ONE)).divide(exchangeRate, 3, BigDecimal.ROUND_DOWN);
    	
		if(price.compareTo(minPrice) > 0 && price.compareTo(maxPrice) < 0) {
			return price ;
		}
		BigDecimal firstValue = (maxPrice.add(minPrice)).divide(BigDecimal.valueOf(2), 2, BigDecimal.ROUND_DOWN);
		BigDecimal secondValue = (maxPrice.add(minPrice)).divide(BigDecimal.valueOf(2), 2, BigDecimal.ROUND_UP);
		
		return (firstValue.multiply(exchangeRate).divideAndRemainder((BigDecimal.valueOf(10)))[1].intValue() < 9) ? secondValue : firstValue ;
    }
    
    /**************************************************************************comments******************************************************************************************/
   
    public ProductComments persistComments(ProductComments request) {
    	
    	if(request.getId() != null) {
    		ProductComments source = productCommentsRepository.findById(request.getId()).get();
    		Preconditions.checkArgument(source != null, "要修改的数据不存在");
    		
    		BeanUtil.copyProperties(request, source, "id");
    		return productCommentsRepository.save(source);
    	}
    	return productCommentsRepository.save(request);
    }
    
    public void deleteComments(List<Long> commentIds) {
    	
    	for (Long commentId : commentIds) {
    		productCommentsRepository.deleteById(commentId);
		}
    	
    }
    
    public List<ProductComments> searchComments(Long productId) {
    	
    	List<ProductComments> comments = productCommentsRepository.findByProductId(productId, Sort.by(Direction.DESC, "id"));
    	
    	return comments ; 
    }
    
    /**************************************************************************comments******************************************************************************************/
    
}
